package com.umessage.common.shiro;

import java.util.ArrayList;
import java.util.Collection;
import java.util.LinkedHashMap;
import java.util.List;

import javax.servlet.Filter;

import org.apache.commons.lang.StringUtils;
import org.apache.shiro.codec.Base64;
import org.apache.shiro.mgt.SecurityManager;
import org.apache.shiro.realm.Realm;
import org.apache.shiro.session.SessionListener;
import org.apache.shiro.spring.LifecycleBeanPostProcessor;
import org.apache.shiro.spring.security.interceptor.AuthorizationAttributeSourceAdvisor;
import org.apache.shiro.spring.web.ShiroFilterFactoryBean;
import org.apache.shiro.web.mgt.CookieRememberMeManager;
import org.apache.shiro.web.mgt.DefaultWebSecurityManager;
import org.apache.shiro.web.servlet.SimpleCookie;
import org.apache.shiro.web.session.mgt.DefaultWebSessionManager;
import org.crazycake.shiro.RedisCacheManager;
import org.crazycake.shiro.RedisManager;
import org.crazycake.shiro.RedisSessionDAO;
import org.springframework.aop.framework.autoproxy.DefaultAdvisorAutoProxyCreator;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.DependsOn;

import com.umessage.common.config.FebsProperies;
import com.umessage.common.listener.ShiroSessionListener;

import at.pollux.thymeleaf.shiro.dialect.ShiroDialect;

/**
 * Shiro 配置类
 *
 * @author MrBird
 */
@Configuration
public class ShiroConfig {

	@Autowired
	private FebsProperies febsProperies;

	@Value("${spring.redis.host}")
	private String host;

	@Value("${spring.redis.port}")
	private int port;

	@Value("${spring.redis.password}")
	private String password = "";

	@Value("${spring.redis.timeout}")
	private int timeout;

	/**
	 * shiro 中配置 redis 缓存
	 *
	 * @return RedisManager
	 */
	private RedisManager redisManager() {
		RedisManager redisManager = new RedisManager();
		// 缓存时间，单位为秒
		redisManager.setExpire(febsProperies.getShiro().getExpireIn());
		redisManager.setHost(host);
		redisManager.setPort(port);
		if (StringUtils.isNotBlank(password))
			redisManager.setPassword(password);
		redisManager.setTimeout(timeout);
		return redisManager;
	}

	private RedisCacheManager cacheManager() {
		RedisCacheManager redisCacheManager = new RedisCacheManager();
		redisCacheManager.setRedisManager(redisManager());
		return redisCacheManager;
	}

	@Bean
	public ShiroFilterFactoryBean shiroFilterFactoryBean(SecurityManager securityManager) {
		ShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean();
		// 设置 securityManager
		shiroFilterFactoryBean.setSecurityManager(securityManager);
		// 登录的 url
		shiroFilterFactoryBean.setLoginUrl(febsProperies.getShiro().getLoginUrl());
		// 登录成功后跳转的 url
		shiroFilterFactoryBean.setSuccessUrl(febsProperies.getShiro().getSuccessUrl());
		// 未授权 url
		shiroFilterFactoryBean.setUnauthorizedUrl(febsProperies.getShiro().getUnauthorizedUrl());

		LinkedHashMap<String, Filter> filtersMap = new LinkedHashMap<String, Filter>();
		filtersMap.put("restfilter", new JwtFilter());
//        filtersMap.put("restfilter", new RestfulAuthenticationFilter());

		shiroFilterFactoryBean.setFilters(filtersMap);

		LinkedHashMap<String, String> filterChainDefinitionMap = new LinkedHashMap<>();
		// 设置免认证 url
		String[] anonUrls = StringUtils.splitByWholeSeparatorPreserveAllTokens(febsProperies.getShiro().getAnonUrl(),
				",");
		for (String url : anonUrls) {
			filterChainDefinitionMap.put(url, "anon");
		}
		filterChainDefinitionMap.put("/rest/**", "restfilter");
		// 配置退出过滤器，其中具体的退出代码 Shiro已经替我们实现了
		filterChainDefinitionMap.put(febsProperies.getShiro().getLogoutUrl(), "logout");
		// 除上以外所有 url都必须认证通过才可以访问，未通过认证自动访问 LoginUrl
		filterChainDefinitionMap.put("/**", "user");

		shiroFilterFactoryBean.setFilterChainDefinitionMap(filterChainDefinitionMap);

		return shiroFilterFactoryBean;
	}

	@Bean
	public SecurityManager securityManager() {
		DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();
//        securityManager.setAuthenticator(new CustomizedModularRealmAuthenticator());

//       // 配置 SecurityManager，并注入 shiroRealm
//        securityManager.setRealm(shiroRealm());

		List<Realm> realmList = new ArrayList<Realm>();
//        realmList.add(new MPClientRealm());
//        realmList.add(new MPHotelRealm());
		realmList.add(shiroRealm());
		realmList.add(jwtRealm());
		realmList.add(jwtMemberRealm());
		securityManager.setRealms(realmList);

		// 配置 rememberMeCookie
		securityManager.setRememberMeManager(rememberMeManager());
		// 配置 缓存管理类 cacheManager
		securityManager.setCacheManager(cacheManager());
//        securityManager.setSessionManager(sessionManager());
		return securityManager;
	}

	@Bean(name = "lifecycleBeanPostProcessor")
	public static LifecycleBeanPostProcessor lifecycleBeanPostProcessor() {
		// shiro 生命周期处理器
		return new LifecycleBeanPostProcessor();
	}

	@Bean
	public ShiroRealm shiroRealm() {
		// 配置 Realm，需自己实现，见 com.umessage.common.shiro.ShiroRealm
		return new ShiroRealm();
	}

	@Bean
	public JwtRealm jwtRealm() {
		// 配置 Realm，需自己实现，见 com.umessage.common.shiro.ShiroRealm
		return new JwtRealm();
	}

	@Bean
	public JwtMemberRealm jwtMemberRealm() {
		return new JwtMemberRealm();
	}

	/**
	 * rememberMe cookie 效果是重开浏览器后无需重新登录
	 *
	 * @return SimpleCookie
	 */
	private SimpleCookie rememberMeCookie() {
		// 设置 cookie 名称，对应 login.html 页面的 <input type="checkbox" name="rememberMe"/>
		SimpleCookie cookie = new SimpleCookie("rememberMe");
		// 设置 cookie 的过期时间，单位为秒，这里为一天
		cookie.setMaxAge(febsProperies.getShiro().getCookieTimeout());
		return cookie;
	}

	/**
	 * cookie管理对象
	 *
	 * @return CookieRememberMeManager
	 */
	private CookieRememberMeManager rememberMeManager() {
		CookieRememberMeManager cookieRememberMeManager = new CookieRememberMeManager();
		cookieRememberMeManager.setCookie(rememberMeCookie());
		// rememberMe cookie 加密的密钥
		cookieRememberMeManager.setCipherKey(Base64.decode("4AvVhmFLUs0KTA3Kprsdag=="));
		return cookieRememberMeManager;
	}

	/**
	 * DefaultAdvisorAutoProxyCreator 和 AuthorizationAttributeSourceAdvisor 用于开启
	 * shiro 注解的使用 如 @RequiresAuthentication， @RequiresUser， @RequiresPermissions 等
	 *
	 * @return DefaultAdvisorAutoProxyCreator
	 */
	@Bean
	@DependsOn({ "lifecycleBeanPostProcessor" })
	public DefaultAdvisorAutoProxyCreator advisorAutoProxyCreator() {
		DefaultAdvisorAutoProxyCreator advisorAutoProxyCreator = new DefaultAdvisorAutoProxyCreator();
		advisorAutoProxyCreator.setProxyTargetClass(true);
		return advisorAutoProxyCreator;
	}

	@Bean
	public AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor(SecurityManager securityManager) {
		AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor = new AuthorizationAttributeSourceAdvisor();
		authorizationAttributeSourceAdvisor.setSecurityManager(securityManager);
		return authorizationAttributeSourceAdvisor;
	}

	/**
	 * 用于开启 Thymeleaf 中的 shiro 标签的使用
	 *
	 * @return ShiroDialect shiro 方言对象
	 */
	@Bean
	public ShiroDialect shiroDialect() {
		return new ShiroDialect();
	}

	@Bean
	public RedisSessionDAO redisSessionDAO() {
		RedisSessionDAO redisSessionDAO = new RedisSessionDAO();
		redisSessionDAO.setRedisManager(redisManager());
		return redisSessionDAO;
	}

	/**
	 * session 管理对象
	 *
	 * @return DefaultWebSessionManager
	 */
	@Bean
	public DefaultWebSessionManager sessionManager() {
		DefaultWebSessionManager sessionManager = new MyWebSessionManager();
		Collection<SessionListener> listeners = new ArrayList<>();
		listeners.add(new ShiroSessionListener());
		// 设置session超时时间，单位为毫秒
		sessionManager.setGlobalSessionTimeout(febsProperies.getShiro().getSessionTimeout());
		sessionManager.setSessionListeners(listeners);
		sessionManager.setSessionDAO(redisSessionDAO());
		return sessionManager;
	}
}
